class ImageFact < ActiveRecord::Base

  # sha1 value for the imaginary "Plimsol object" against which any tag relevancy is compared as a border between representative and non-representative.
  # This is a member of the family of McGuffin objects.
  PLIMSOLL_SHA1 = 'a7edacdca8ad0520bbe7267c8840781b6f33b153';

  validates_uniqueness_of :sha1
  validates_presence_of :sha1
#  validates_presence_of :ihash
#  validates_numericality_of :ihash, :only_integer => true, :greater_than_or_equal_to => (-(2**63)), :less_than => 2**63
  validates_length_of :sha1, :is => 40
  
  has_many :images, :dependent => :destroy
  has_many :votes, :dependent => :destroy
  
  def width
    @width ||= Vote.fetch(self, Tag::WIDTH) { |v| v.plus_votes }
  end
  
  def height
    @height ||= Vote.fetch(self, Tag::HEIGHT) { |v| v.plus_votes }
  end

  # You can get width and multiply by height.. if you won't use 'em though, this saves a DB call or so. :3
  # It's stored in the votes db of course so that I can sort by it.
  def area
    @area ||= Vote.fetch(self, Tag::AREA) { |v| v.plus_votes }
  end
  
  def aspect
    @aspect ||= 1.0*self.width / self.height
  end

  def size
    @size ||= Vote.fetch(self, Tag::SIZE) { |v| v.plus_votes }
  end
  
  def plimsoll?
    self.sha1 == PLIMSOLL_SHA1
  end

  def self.plimsoll_fact
    @plimsoll_fact = find_by_sha1 PLIMSOLL_SHA1
  end
  
  # gets a random ImageFact that HAS an associated image.
  def self.random(n=1)
    ImageFact.find(:all, :conditions => "exists (select 1 from images where image_fact_id = image_facts.id)", :limit => n, :order => "random()")
  end
  
  # takes or assumes a polynomial, takes or randomly generates an input, and uses the polycalc output to determine how strongly tagged the resulting image must be.
  # Output values < 0 yield randomly chosen, thus probably untagged images.
  # Output is clamped to an upper bound of 1, which always displays the most heavily tagged image: probably best to avoid that result.
  # delivers exactly one ImageFact (which contains 1 or more matching Image) as a result.
  # tagid is assumed already valid/invoked
  def self.tag_sample(tagid, options=nil)
    options ||= {}
    options[:polynomial]    ||= []    # Array representing polynomial randomness interpreter
    options[:polynomial][0] ||= -0.4  
    options[:polynomial][1] ||= 1.4
    options[:randval]       ||= rand  # In case someone needs to force a feed-in value for some purpose
    options[:blocklist]     ||= []    # list of image_fact_id's to not consider
    result = [1.0, polycalc(options[:randval], options[:polynomial])].min
    
#    throw result.to_s
    
    if(result<0.0)
      res = self.random[0]  # <0 result, call our own handy "random" function with the implicit n=1 to get one image choice.
    else
      res = ImageFact.find_by_sql ["select f.* from image_facts f, votes v, images i where i.image_fact_id=f.id and v.image_fact_id=f.id and "+
          "v.tag_id=? and f.id not in (#{options[:blocklist].join ','}) order by abs(perc-?) limit 1", tagid, result*1e6]
      res = res.empty? ? self.random[0] : res[0]  # if nothing's tagged, just return a random choice.
    end
  end

  # This belongs in some other scope #@$@%@#%$#%$#%  
  # accepts an input value and an array of polynomial coefficients.
  # produces output of polynomial calculation. All values are cast to float and result is float.
  # ex: polycalc(0.75 [-1, 6, -2]) = ( -2 * 0.75^2 + 6 * 0.75 - 1) = 2.375
  def self.polycalc(x, poly=[])
    sum=0
    poly.each_with_index { |co,ex| sum+=co.to_f*(x.to_f**ex.to_f) }
    sum
  end

end
